package nickthissen.iracing.parsing;

import android.text.TextUtils;

import org.apache.commons.lang3.exception.ExceptionUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.nodes.Node;
import org.jsoup.parser.Tag;
import org.jsoup.select.Elements;

import java.util.ArrayList;

import nickthissen.iracing.common.Donations;
import nickthissen.iracing.common.Urls;
import nickthissen.iracing.datamodels.*;

/**
 * Created by Nick on 19-5-13.
 */
public class ThreadParser extends Parser
{
    private static final String STAFF_URL = "/jforum/images/avatar/staff2.png";
    private static final String STAFF_ALT = "[Avatar]";

    @Override
    protected String getLogName()
    {
        return "ThreadParser";
    }

    @Override
    protected Result createDefaultResultObject()
    {
        Result result = new Result();
        PostList postlist = new PostList();
        postlist.Posts = new ArrayList<Post>();
        result.Object = postlist;
        result.Message = "Error parsing thread.";
        return result;
    }

    @Override
    public Result doParse(String source)
    {
        Result result = new Result();
        PostList postlist = new PostList();
        postlist.Posts = new ArrayList<Post>();

        log("Start jsoup parsing thread");
        Document doc = Jsoup.parse(source);
        log("Finished jsoup parsing thread.");

        parseThread(postlist, doc);
        parsePagination(postlist, doc);
        parsePoll(postlist, doc);

        log("PollHtml: " + postlist.PollHtml);

        Element tableNode = doc.getElementById("forum");

        // Loop through rows of table
        Element tbody = tableNode.select("tbody").first();
        ArrayList<Element> rows = Parsing.selectChildren(tbody, "tr");
        int start = postlist.PollHtml == null ? 1 : 3;

        int increment = 3; // Increment changes when ignored user has posted in this thread
        for (int trIndex = start; trIndex < rows.size(); trIndex += increment)
        {
            // Each post consists of a set of three rows
            // Row 1: dark header above post, contains date & edit / quote buttons
            // Row 2: contains author & content
            // Row 3: footer, not interesting

            Element tr1 = rows.get(trIndex);
            Element tr2 = rows.get(trIndex + 1);

            log("Row1: " + tr1.toString());
            log("Row2: " + tr2.toString());

            Post post = new Post();

            parseDateAndActions(postlist, post, tr1);

            // Check for ignored user
            if (post.IsIgnored)
            {
                increment = 2; // next iteration, skip only 2 rows instead of 3
            }
            else
            {
                increment = 3;

                //Elements columns = tr2.select("td");
                ArrayList<Element> columns = Parsing.selectChildren(tr2, "td");
                Element td1 = columns.get(0);
                Element td2 = columns.get(1);

                log("Col1: " + td1.toString());
                log("Col2: " + td2.toString());

                parseAuthor(post, td1);
                parseContent(post, td2);
            }

            postlist.Posts.add(post);
        }

        parseWatching(doc, postlist);

        result.Success = true;

        result.Object = postlist;
        return result;
    }

    private void parseThread(PostList postlist, Document doc)
    {
        log("Start parsing thread/forum details.");

        Elements divs = doc.select("div[class=subContent]");
        if (divs.size() == 0)
        {
            log("Error: subContent div not found.");
            return;
        }

        Element div = divs.get(0);
        ArrayList<Element> links = Parsing.selectChildren(div, "a");
        //Elements links = div.select("a");
        if (links.size() < 3)
        {
            log("Error: less than 3 links found.");
            return;
        }

        Element forumNode = links.get(1);
        Element authorNode = links.get(2);
        log("Forumnode: " + forumNode.toString());
        log("Authornode: " + authorNode.toString());

        String url = forumNode.attr("href");
        postlist.ForumUrl = url;
        postlist.ForumId = Integer.parseInt(Parsing.getFilenameWithoutExtension(url));
        postlist.ForumTitle = forumNode.text();

        url = authorNode.attr("href");
        postlist.ThreadUrl = url;
        postlist.ThreadId = Integer.parseInt(Parsing.getFilenameWithoutExtension(url));
        postlist.ThreadTitle = authorNode.text();
    }

    private void parsePoll(PostList postlist, Document doc)
    {
        log("Start parsing poll.");
        Elements pollTables = doc.select("table[id=poll]");
        if (pollTables.size() > 0)
        {
            Element pollTable = pollTables.first();
            postlist.PollHtml = pollTable.outerHtml();
        }
    }

    private void parseDateAndActions(PostList postlist, Post post, Element node)
    {
        log("Start parsing date/actions: " + node);

        //Elements columns = node.select("td");
        ArrayList<Element> columns = Parsing.selectChildren(node, "td");
        Element dateNode = columns.get(0);
        Element actionsNode = columns.get(1);
        log("Datenode: " + dateNode);
        log("Actionsnode: " + actionsNode);

        // Check for ignored
        Elements ignoreNodes = actionsNode.select("div[class=ignoreMessage]");
        if (ignoreNodes.size() > 0)
        {
            post.IsIgnored = true;
            post.IgnoredText = ignoreNodes.get(0).text();
            post.CanEdit = false;
            return;
        }

        Element scriptNode = dateNode.child(0).child(0);
        log("Scriptnode: " + scriptNode);

        String dateString = scriptNode.toString();
        long unixMs = this.parseDateTimeScript(dateString);
        post.CreatedTime = unixMs;

        Element subjectNode = actionsNode.select("span[class=postSubject]").first();
        log("Subjectnode: " + subjectNode);
        Element subjectLink = subjectNode.select("a").first();
        log("Subjectlink: " + subjectLink);

        post.Id = Integer.parseInt(subjectLink.attr("name"));

        Element actionsDiv = actionsNode.select("div[class=postActions]").first();
        log("Actionsdiv: " + actionsDiv);

        ArrayList<Element> links = Parsing.selectChildren(actionsDiv, "a");
        //Elements links = actionsDiv.select("a");
        log("Actionsdiv links: " + links);

        if (links.size() == 0)
        {
            post.CanEdit = false;
            postlist.CanReply = false;
        }
        else if (links.size() == 1)
        {
            postlist.CanReply = true;
            post.CanEdit = false;
            post.QuoteUrl = HttpUtil.htmlDecode(links.get(0).attr("href"));
        }
        else
        {
            postlist.CanReply = true;
            post.CanEdit = true;
            post.EditUrl = HttpUtil.htmlDecode(links.get(0).attr("href"));
            post.QuoteUrl = HttpUtil.htmlDecode(links.get(1).attr("href"));
        }
    }

    private void parseAuthor(Post post, Element node)
    {
        log("Start parsing author: " + node);
        Elements childs = node.children();
        Element childNode = childs.get(1);
        if (childNode.nodeName().equals("img") && childNode.attr("src").endsWith("iconOnline.png"))
        {
            post.AuthorOnline = true;
            childNode = childs.get(2);
        }

        post.AuthorUrl = childNode.attr("href");
        post.Author = childNode.child(0).text();

        post.AuthorDonated = Donations.hasDonated(post.Author);

        ArrayList<Element> imgs = Parsing.selectChildren(node, "img");
        post.AuthorIsStaff = Linq.any(imgs, new Predicate<Element>()
        {
            @Override
            public boolean apply(Element in)
            {
                return in.attr("src").endsWith("/jforum/images/avatar/staff2.png");
            }
        });
    }

    private void parseContent(Post post, Element node)
    {
        log("Start parsing content.");
        Elements childs = node.children();
        if (childs.size() == 0)
        {
            log("No childs.");
            return;
        }

        // Set contents as HTML
        Element child = childs.first();
        post.Contents = child.html();

        // Remove all tags and set as plain contents
        post.PlainContents = TagSanitizer.removeAllTags(child);

        fixContentTags(post);
    }

    private void fixContentTags(Post post)
    {
        log("Start fixing content tags.");

        Document doc = Jsoup.parse(post.Contents.trim());

        // Fix video tags
        fixVideoTags(doc);

        // Clean unwanted elements
        doc = TagSanitizer.sanitize(doc);

        // Fix image tags
        fixImageTags(doc);

        // Fix relative URLs
        fixRelativeUrls(doc);

        // Save result
        post.Contents = doc.body().html();
    }

    private void parseWatching(Document doc, PostList postlist)
    {
        Elements watch = doc.select("div[class=contentBar] > a[id=watchThisTopic]");
        Elements unwatch = doc.select("div[class=contentBar] > a[id=stopWatching]");
        if (watch.size() == 0 && unwatch.size() == 1)
            postlist.IsWatching = true;
        else
            postlist.IsWatching = false;
    }
}
